{ "cells": [ { "cell_type": "markdown", "id": "dc15734d-8749-49ae-be80-c3392e9699e1", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "# Time Evolution\n", "\n", "Time evolutions in `quimb` are handled by the class {class}`~quimb.evo.Evolution`, which is initialized with a starting state and hamiltonian.\n", "\n", "## Basic Usage\n", "\n", "Set up the {class}`~quimb.evo.Evolution` object with a initial state and hamiltonian." ] }, { "cell_type": "code", "execution_count": 1, "id": "1330c133-a456-4273-b4e9-0f5edab0040d", "metadata": {}, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "import quimb as qu\n", "\n", "p0 = qu.rand_ket(2**10)\n", "h = qu.ham_heis(10, sparse=True)\n", "evo = qu.Evolution(p0, h)" ] }, { "cell_type": "markdown", "id": "bad44ff5-2dcc-43e1-912c-aff549e44221", "metadata": {}, "source": [ "Update it in a single shot to a new time and get the state," ] }, { "cell_type": "code", "execution_count": 2, "id": "2edbe41e-48c8-4e69-b393-f3dbce606845", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[ 0.027396-0.00772j ]\n", " [ 0.00786 -0.019236j]\n", " [ 0.00165 +0.021386j]\n", " ...\n", " [-0.04785 -0.028168j]\n", " [ 0.006677+0.038223j]\n", " [ 0.011585-0.006924j]]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "evo.update_to(1)\n", "evo.pt" ] }, { "cell_type": "markdown", "id": "b5764132-0502-4650-a401-0c83f4cf23d6", "metadata": {}, "source": [ "Lazily generate the state at multiple times:" ] }, { "cell_type": "code", "execution_count": 3, "id": "2b138fbe-6ac1-4ceb-beb8-6b5c2e3e0184", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.0003167209578964015\n", "0.0033296109901839177\n", "0.0012326190125073922\n" ] } ], "source": [ "for pt in evo.at_times([2, 3, 4]):\n", " print(qu.expec(pt, p0))" ] }, { "cell_type": "markdown", "id": "3c8ea54a-ac64-4afd-81ef-7e87608aaaef", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "## Methods of Updating\n", "\n", "There are three methods of updating the state:\n", "\n", "> - `Evolution(..., method='integrate')`: use definite integration.\n", "> Get system at each time step, only need action of Hamiltonian on\n", "> state. Generally efficient. For pure and mixed states. The\n", "> additional option `int_small_step={False, True}` determines\n", "> whether a low or high order adaptive stepping scheme is used,\n", "> giving naturally smaller or larger times steps. See\n", "> {class}`scipy.integrate.ode` for details, `False` corresponds\n", "> to `\"dop853\"`, `True` to `\"dopri5\"`.\n", "> - `Evolution(..., method='solve')`. Diagonalize the hamiltonian,\n", "> which once done, allows quickly updating to arbitrary times.\n", "> Supports pure and mixed states, recomended for small systems.\n", "> - `Evolution(..., method='expm')`: compute the evolved state\n", "> using the action of the matrix exponential in a 'single shot'\n", "> style. Only needs action of Hamiltonian, for very large systems\n", "> can use distributed MPI. Only for pure states.\n", "\n", "## Computing on the fly\n", "\n", "Sometimes, if integrating, it is best to just query the state at time-steps chosen dynamically by the adaptive scheme. This is achieved using the `compute` keyword supplied to `Evolution`. It can also just be a convenient way to set up calculations as well:" ] }, { "cell_type": "code", "execution_count": 4, "id": "0dc964f7-5ac2-44ad-aaeb-320609078861", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|##########| 100/100 [00:00<00:00, 20516.06%/s]\n" ] } ], "source": [ "p0 = qu.rand_product_state(10)\n", "h = qu.ham_heis(10, sparse=True)\n", "\n", "dims = [2] * 10\n", "sysa, sysb = (0, 1), (2, 3)\n", "\n", "\n", "def calc_t_and_logneg(t, pt):\n", " ln = qu.logneg_subsys(pt, dims, sysa, sysb)\n", " return t, ln\n", "\n", "\n", "evo = qu.Evolution(p0, h, compute=calc_t_and_logneg, progbar=True)\n", "evo.update_to(1)\n", "\n", "ts, lns = zip(*evo.results)" ] }, { "cell_type": "code", "execution_count": 5, "id": "2387b525-54e8-4dca-ae42-8d5eca0b9916", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.0,\n", " 0.2494153162899183,\n", " 0.4809058736983094,\n", " 0.7202389885285744,\n", " 0.9908548077660357,\n", " 1.0)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ts" ] }, { "cell_type": "code", "execution_count": 6, "id": "0908d890-675e-449e-aa3c-a82c7d03aab8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.0,\n", " 0.07922473340252047,\n", " 0.19984430125848626,\n", " 0.3726546674413778,\n", " 0.5937245957372282,\n", " 0.6011504266800122)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lns" ] }, { "cell_type": "markdown", "id": "2636ba38-c42b-4f56-b495-e968c8d8d464", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "If a dict of callables is supplied to `compute`, (each should take two arguments, the time, and the state, as above), `Evolution.results` will itself be a dictionary containing the results of each function at each time step, under the respective key. This can be more convenient:" ] }, { "cell_type": "code", "execution_count": 7, "id": "55c0a8d7-ad7b-43ff-8b6d-ff60560001f6", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|##########| 100/100 [00:00<00:00, 22323.19%/s]\n" ] } ], "source": [ "def calc_t(t, _):\n", " return t\n", "\n", "\n", "def calc_logneg(_, pt):\n", " return qu.logneg_subsys(pt, [2] * 10, 0, 1)\n", "\n", "\n", "evo = qu.Evolution(\n", " p0, h, compute={\"t\": calc_t, \"ln\": calc_logneg}, progbar=True\n", ")\n", "evo.update_to(1)" ] }, { "cell_type": "code", "execution_count": 8, "id": "972f485b-64c4-46e5-86b4-16cbd93d81a2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'t': [0.0,\n", " 0.2494153162899183,\n", " 0.4809058736983094,\n", " 0.7202389885285744,\n", " 0.9908548077660357,\n", " 1.0],\n", " 'ln': [0.0,\n", " 0.2670014309042237,\n", " 0.4550737777089945,\n", " 0.5894993607543968,\n", " 0.6676819999827515,\n", " 0.66894221219201]}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "evo.results" ] }, { "cell_type": "markdown", "id": "d904d5df-f633-4b96-98cd-234f326e98fa", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "(time-dependent-evolution)=\n", "\n", "## Time-Dependent Evolutions\n", "\n", "If you are using `method='integrate'` you can supply a callable to `ham` to evolve the state with a time dependent Hamiltonian. It should take a single argument `t` and return the Hamiltonian at the time. It probably makes sense to use a custom class here to avoid reconstructing as much of the Hamiltonian as possible at each step.\n", "\n", "Here we'll evolve the Neel state:\n", "\n", "$$\n", "| \\psi(0) \\rangle = | \\uparrow \\downarrow \\uparrow \\downarrow \\uparrow \\ldots \\rangle\n", "$$\n", "\n", "with the Hamiltonian:\n", "\n", "$$\n", "H(t) = \\sum_{i = 0}^{L - 1} S^Z_{i} S^Z_{i + 1} + \\cos(t) \\sum_{i}^{L} S^X_i\n", "$$" ] }, { "cell_type": "code", "execution_count": 9, "id": "c64f70b8-a689-4fc9-ac49-eb90e2c5921c", "metadata": {}, "outputs": [], "source": [ "class MyTimeDepIsingHam:\n", " def __init__(self, L):\n", " self.h_interaction = qu.ham_ising(\n", " L, sparse=True, jz=1.0, bx=0.0, cyclic=False\n", " )\n", " self.h_field = qu.ham_ising(\n", " L, sparse=True, jz=0.0, bx=1.0, cyclic=False\n", " )\n", "\n", " def __call__(self, t):\n", " return self.h_interaction + qu.cos(t) * self.h_field" ] }, { "cell_type": "code", "execution_count": 10, "id": "1480b979-aa44-4726-b01d-830f767613e4", "metadata": {}, "outputs": [], "source": [ "L = 16\n", "\n", "# our initial state\n", "psi0 = qu.neel_state(L)\n", "\n", "# instantiate the ham object, it's __call__ method will be used by Evolution\n", "fn_ham_t = MyTimeDepIsingHam(L)" ] }, { "cell_type": "markdown", "id": "0a86b6c6-8a02-44b0-ac25-85f074af69c5", "metadata": {}, "source": [ "We still want to compute some properties during the evolution:" ] }, { "cell_type": "code", "execution_count": 11, "id": "67a204ea-4f14-435d-b68d-746756ad77e1", "metadata": {}, "outputs": [], "source": [ "compute = {\n", " \"time\": lambda t, p: t,\n", " \"entropy\": lambda t, p: qu.entropy_subsys(\n", " p, dims=[2] * L, sysa=range(L // 2)\n", " ),\n", "}" ] }, { "cell_type": "markdown", "id": "256af3bb-af91-4139-bfa9-68f6703403c9", "metadata": {}, "source": [ "Now we set up the evolution object again:" ] }, { "cell_type": "code", "execution_count": 12, "id": "e2608200-f85e-461f-b347-116b8b464d24", "metadata": {}, "outputs": [], "source": [ "evo = qu.Evolution(psi0, fn_ham_t, progbar=True, compute=compute)" ] }, { "cell_type": "code", "execution_count": 13, "id": "d26978f4-1fb2-4f8a-b0d6-1338fff320f6", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|##########| 100/100 [00:13<00:00, 7.64%/s]\n" ] } ], "source": [ "evo.update_to(10)" ] }, { "cell_type": "markdown", "id": "4735d921-676f-4db6-8dc8-573b42f44f3f", "metadata": {}, "source": [ "We can plot the half chain entropy that we computed on the fly:" ] }, { "cell_type": "code", "execution_count": 14, "id": "e4d8cb4e-16ab-47dd-8a01-e0c91836b5dc", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "text/plain": [ "[]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qu.plot(evo.results[\"time\"], evo.results[\"entropy\"])" ] }, { "cell_type": "markdown", "id": "59aa8c6a-e5ee-4720-a132-f54966e3a9d7", "metadata": {}, "source": [ "Or we can use the final state:" ] }, { "cell_type": "code", "execution_count": 15, "id": "81f8058b-7128-42f5-9e70-56699dc6ff32", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.float64(0.003302180752068547)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "qu.fidelity(psi0, evo.pt)" ] } ], "metadata": { "celltoolbar": "Raw Cell Format", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3" } }, "nbformat": 4, "nbformat_minor": 4 }